﻿using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace System
{
  [DebuggerStepThrough]
  public static class ListExtensions
  {
    public static IList<TSource> Clone<TSource>(this IList<TSource> listToClone) where TSource : ICloneable
    {
      return listToClone.Select(item => (TSource)item.Clone()).ToList();
    }
    public static List<TResult> Clone<TSource, TResult>(this List<TSource> listSource) where TResult : new()
    {
      var newList = new List<TResult>();
      listSource.ForEach((item) => { newList.Add(Copy<TResult, TSource>(item)); });
      return newList;
    }
    //public static void CloneTo<T, K>(this List<T> oldList, List<K> newList) where K : new()
    //{
    //  oldList.ForEach((item) => { newList.Add(Copy<K, T>(item)); });
    //}
    public static TResult Copy<TResult, TSource>(TSource obj) where TResult : new()
    {
      if (obj == null)
        return default(TResult);

      var objResult = new TResult();
      foreach (PropertyInfo prop in obj.GetType().GetProperties())
      {
        if (!prop.IsPrimitiveType())
          continue;

        PropertyInfo propOther = objResult.GetType().GetProperty(prop.Name);
        if (propOther != null)
        {
          propOther.SetValue(objResult, prop.GetValue(obj, null), null);
        }
      }
      return objResult;
    }

    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
      foreach (T item in source)
      {
        action(item);
      }
    }

    public static T BinarySearch<T, TKey>(this IList<T> list, Func<T, TKey> keySelector, TKey key) where TKey : IComparable<TKey>
    {
      int min = 0;
      int max = list.Count;

      while (min < max)
      {
        int mid = (max + min) / 2;
        T midItem = list[mid];
        TKey midKey = keySelector(midItem);
        int comp = midKey.CompareTo(key);
        if (comp < 0)
        {
          min = mid + 1;
        }
        else if (comp > 0)
        {
          max = mid - 1;
        }
        else
        {
          return midItem;
        }
      }

      if (min == max && keySelector(list[min]).CompareTo(key) == 0)
      {
        return list[min];
      }

      throw new InvalidOperationException("Item not found");
    }

    public static string Join<T>(this IEnumerable<T> collection, Func<T, string> func, string separator)
    {
      return String.Join(separator, collection.Select(func).ToArray());
    }

    public static bool IsIn<T>(this T source, params T[] list)
    {
      if (null == source) throw new ArgumentNullException("source");
      return list.Contains(source);
    }
    public static bool IsNotIn<T>(this T source, params T[] list)
    {
      if (null == source) throw new ArgumentNullException("source");
      return !list.Contains(source);
    }

    }
}